/*
 * Copyright (C) 2006-2015
 * 版权所有者为北京捷越联合信息咨询有限公司。本系统是商用软件,未经授权擅自复制或传播本程序的部分或全部将是非法的。
 * @title: HBFXCalculate.java
 * @package com.jy.modules.cims.component.credit.creditor.common
 * @author zhaox
 * @date 2015-7-21 下午7:19:40
 * @version v1.00
 * @description: TODO(用一句话描述该文件做什么)
 */

package com.jy.modules.cims.component.credit.creditor.common;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.jy.modules.cims.common.sysUtils.DateTimeUtils;
import com.jy.modules.cims.data.dao.RepaymentPlanDao;
import com.jy.modules.cims.data.dto.credit.CreditSettleParam;

/**
 * @classname: HBFXCalculate
 * @description: TODO(这里用一句话描述这个类的作用)
 */
@Service("com.jy.modules.cims.component.credit.creditor.common.XXHBCalculate")
public class XXHBCalculate implements ICalculateSettlePlan {

    @Autowired
    private RepaymentPlanDao repaymentPlanDao;

    private Map refundMap = new HashMap();

    /* (非 Javadoc)
    * <p>title: calculate</p>
    * <p>description: </p>
    * @param creditSettleParam
    * @return
    * @see com.jy.modules.cims.component.credit.creditor.common.ICalculateSettlePlan#calculate(com.jy.modules.cims.data.dto.credit.CreditSettleParam)
    */

    @Override
    public List calculate(CreditSettleParam creditSettleParam) throws Exception {
        Map paramMap = new HashMap();
        paramMap.put("credit_code", creditSettleParam.getCreditCode());
        paramMap.put("transfer_id", creditSettleParam.getTransferId());
        paramMap.put("prompty_date", DateTimeUtils.formatDate(creditSettleParam.getPromptyDate(), "yyyyMMdd"));//交割日
        // 初始化结算计划
        // 全额匹配01
        // 本金匹配02
        // 全额返还03
        int term = creditSettleParam.getTerm();//总期数
        int surplusTerm = creditSettleParam.getSurplusTerm();//剩余期数
        // 从第几期开始生成结算计划,这个是第一期，结算金额计算方法不同
        int beginTerm = term - surplusTerm + 1;
        // 计算首次还款日
        Calendar calendar = Calendar.getInstance();
        Date date = creditSettleParam.getRepayStartDate();//起始还款日期
        calendar.add(Calendar.MONTH, term - surplusTerm);// 设置结算计划的首次还款日
        date.setMonth(calendar.get(Calendar.MONTH));

        List<Map<Object, Object>> list = new ArrayList<Map<Object, Object>>();
        // 查询这只债权的还款计划，（借款人月还本金）用来计算结算计划的结算金额
        list = repaymentPlanDao.selectMatchAmt(paramMap);
        if (list == null)
            throw new Exception("未找到还款计划");
        for (int i = 0; i < list.size(); i++) {
            Map m = (Map) list.get(i);
            refundMap.put(m.get("current_time"), m);
        }
        // 第一期
        Map settleMap = findAmt(creditSettleParam, list);
        if (settleMap == null){
            throw new Exception("未找到匹配的还款计划");
        }
        HashMap<Object, Object> map = new HashMap<Object, Object>();
        creditSettleParam.setMustAmount(new BigDecimal(settleMap.get("MUST_AMOUNT").toString()));
        BigDecimal amt = calculateFirstPrincipal(creditSettleParam);
        BigDecimal interest = calculateFirstInterest(creditSettleParam);
        map.put("term", 1);
        map.put("must_amount", amt);
        map.put("must_accrual", interest);
        map.put("this_repay_sum", amt);
        map.put("credit_code", creditSettleParam.getCreditCode());
        map.put("transfer_id", creditSettleParam.getTransferId());
       // map.put("start_date", DateTimeUtils.formatDateString("yyyyMMdd", date));
        map.put("start_date", DateTimeUtils.formatDate(date, DateTimeUtils.DEFAULT_DATE_TIME_FORMAT));
        map.put("validate_state", "1");
        map.put("match_amount", amt);
        map.put("return_amount", interest);
        list.add(map);
        for (int i = 2; i <= surplusTerm; i++) {
            // 日期+1，计算下一期结算计划
            Date promptyDate = creditSettleParam.getPromptyDate();
            calendar.add(Calendar.MONTH, 1);
            promptyDate.setMonth(calendar.get(Calendar.MONTH));
            creditSettleParam.setPromptyDate(promptyDate);
            findCreditValue(creditSettleParam, list);
            BigDecimal interestAmt = calculateInterest(creditSettleParam);
            map = new HashMap<Object, Object>();
            map.put("term", i);
            map.put("must_amount", 0);
            map.put("must_accrual", interestAmt);
            map.put("this_repay_sum", amt);
            map.put("credit_code", creditSettleParam.getCreditCode());
            map.put("transfer_id", creditSettleParam.getTransferId());
            // 设置结算日期
            calendar.add(Calendar.MONTH, 1);
            date.setMonth(calendar.get(Calendar.MONTH));
            //map.put("start_date", DateTimeUtils.formatDateString("yyyyMMdd", date));
            map.put("start_date", DateTimeUtils.formatDate(date, DateTimeUtils.DEFAULT_DATE_TIME_FORMAT));
            
            map.put("validate_state", "1");
            map.put("match_amount", 0);
            map.put("return_amount", interestAmt);
            list.add(map);
        }
        map = null;
        return list;
    }

    /**
     * 第一个结算日计算结算当月剩余天数
     * 
     * @title: calculateSettleNum
     * @author
     * @description:
     * @date 2015-7-23 上午9:43:59
     * @param creditSettleParam
     * @return
     * @throws
     */
    private int calculateSettleNum(CreditSettleParam creditSettleParam) {
        // 当月剩余天数=当月实际天数-交割日+账单日
        // 当月实际天数
        Calendar a = Calendar.getInstance();
        a.set(Calendar.DATE, 1);// 把日期设置为当月第一天
        a.roll(Calendar.DATE, -1);// 日期回滚一天，也就是最后一天
        int maxDate = a.get(Calendar.DATE);
        // 交割日
        int promptyDate = Integer.valueOf(DateTimeUtils.formatDate( creditSettleParam.getPromptyDate() , "dd"))
                .intValue();
        // 账单日
        int billDate = Integer.valueOf(creditSettleParam.getBillDate()).intValue();
        return maxDate - promptyDate + billDate;
    }

    /**
     * 第一个结算日结算本金
     * 
     * @title: calculatePrincipal
     * @author
     * @description:
     * @date 2015-7-23 上午9:55:16
     * @param creditSettleParam
     * @return
     * @throws
     */
    private BigDecimal calculateFirstPrincipal(CreditSettleParam creditSettleParam) {
        // 结算本金=交割日债权价值-上一账单日债权价值+借款人月还本金
        BigDecimal amt = creditSettleParam.getContractAmt().subtract(creditSettleParam.getCreditValue())
                .subtract(creditSettleParam.getMustAmount());
        return amt.setScale(2, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 第一个结算日结算利息
     * 
     * @title: calculatePrincipal
     * @author
     * @description:
     * @date 2015-7-23 上午9:55:16
     * @param creditSettleParam
     * @return
     * @throws
     */
    private BigDecimal calculateFirstInterest(CreditSettleParam creditSettleParam) {
        BigDecimal principal = calculateFirstPrincipal(creditSettleParam);
        int days = calculateSettleNum(creditSettleParam);
        Calendar a = Calendar.getInstance();
        a.set(Calendar.DATE, 1);
        a.roll(Calendar.DATE, -1);
        int maxDate = a.get(Calendar.DATE);
        BigDecimal amt = principal.multiply(creditSettleParam.getRate()).multiply(new BigDecimal(days))
                .divide(new BigDecimal(12 * maxDate),4);
        return amt;
    }

    /**
     * 计算第二期以后结算金额
     * 
     * @title: calculatePrincipal
     * @author
     * @description:
     * @date 2015-7-23 下午3:30:31
     * @param creditSettleParam
     * @return
     * @throws
     */
    private BigDecimal calculateInterest(CreditSettleParam creditSettleParam) {
        BigDecimal amt = creditSettleParam.getCreditValue().multiply(creditSettleParam.getRate())
                .divide(new BigDecimal(12),2);
        return amt.setScale(2, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 计算赎回本金
     * 
     * @title: calculatePrincipal
     * @author
     * @description:
     * @date 2015-7-28 上午10:37:24
     * @param creditSettleParam
     * @return
     * @throws
     */
    @SuppressWarnings("unused")
	private Map<?, ?> calculateRedemptionPrincipal(CreditSettleParam creditSettleParam) {
        Map<String, String> paramMap = new HashMap<String, String>();
        paramMap.put("credit_code", creditSettleParam.getCreditCode());
        paramMap.put("transfer_id", creditSettleParam.getTransferId());
        paramMap.put("start_date", DateTimeUtils.formatDate(creditSettleParam.getSettleDate(), "yyyy-MM-dd"));
        Map<?, ?> map = repaymentPlanDao.selectPrincipal(paramMap);
        BigDecimal amt = (BigDecimal) map.get("MUST_AMOUNT");

        Map<String, BigDecimal> ret = new HashMap<String, BigDecimal>();
        ret.put("principal", amt);
        // 赎回日
        int redemptionDate = Integer.valueOf(DateTimeUtils.formatDate(creditSettleParam.getRedemptionDate(),"dd"))
                .intValue();
        // 账单日
        int billDate = Integer.valueOf(creditSettleParam.getBillDate()).intValue();
        // 当月实际天数
        Calendar a = Calendar.getInstance();
        a.set(Calendar.DATE, 1);// 把日期设置为当月第一天
        a.roll(Calendar.DATE, -1);// 日期回滚一天，也就是最后一天
        int maxDate = a.get(Calendar.DATE);
        ret.put("interest",
                amt.multiply(creditSettleParam.getRate()).multiply(new BigDecimal(redemptionDate - billDate + 1))
                        .divide(new BigDecimal(12).multiply(new BigDecimal(maxDate)),2));
        return map;
    }

    public BigDecimal matchAmt(CreditSettleParam creditSettleParam) {
        // 应匹配金额=∑▒持有债权(结算本金)
        return new BigDecimal(1);
    }

    public BigDecimal returnAmt(CreditSettleParam creditSettleParam) {
        // 应返还金额=∑▒〖持有债权(〗 结算利息)
        return new BigDecimal(1);
    }

    /**
     * 根据交割日查询当前账单日对应还款计划
     * 
     * @title: findAmt
     * @author
     * @description:
     * @date 2015-7-28 下午2:29:30
     * @param creditSettleParam
     * @param list
     * @return
     * @throws
     */
    public Map findAmt(CreditSettleParam creditSettleParam, List list) {
        // 交割日
        Date promptyDate = creditSettleParam.getPromptyDate();
        int billDate = Integer.parseInt(creditSettleParam.getBillDate());
        // 与交割日月份相同的账单日
        Date date = new Date(promptyDate.getYear(), promptyDate.getMonth(), billDate);
        if (promptyDate.before(date)) {
            String str = DateTimeUtils.formatDate(date, "yyyyMMdd");
            refundMap.get(str);
        } else if (promptyDate.after(date)) {
            date.setMonth(promptyDate.getMonth() + 1);
            String str = DateTimeUtils.formatDate(date, "yyyyMMdd");
            refundMap.get(str);
        }
        date = null;
        return null;
    }

    /**
     * 账单日债权价值
     * 
     * @title: findAmt
     * @author
     * @description:
     * @date 2015-7-28 下午2:29:30
     * @param creditSettleParam
     * @param list
     * @return
     * @throws
     */
    public BigDecimal findCreditValue(CreditSettleParam creditSettleParam, List list) {
        // 交割日
        Date promptyDate = creditSettleParam.getPromptyDate();
        String str = DateTimeUtils.formatDate( promptyDate, "yyyyMMdd");
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
        BigDecimal creditValue = new BigDecimal("0");
        Iterator ite = refundMap.entrySet().iterator();
        while (ite.hasNext()) {
            Entry entry = (Entry) ite.next();
            String current_time = entry.getKey().toString();
            BigDecimal must_amount = new BigDecimal(((Map) entry.getValue()).get("MUST_AMOUNT").toString());
            try {
                // 账单日大于交割日的还款计划本金求和为账单日债权价值
                if (dateFormat.parse(current_time).after(dateFormat.parse(str))) {
                    creditValue = creditValue.add(must_amount);
                }
            }
            catch (ParseException e) {
                e.printStackTrace();
            }
        }
        creditSettleParam.setCreditValue(creditValue);
        dateFormat = null;
        return creditValue;
    }

}
